#include <Windows.h>
#include <cassert>
#include "tmisc.h"
#include "tlib.h"
#include "tapi32v.h"

THashObj::THashObj()
: _priorHash(NULL)
, _nextHash(NULL)
, _hashId(0){}

THashObj::~THashObj()
{
	if ( _priorHash && _priorHash != this ) 
		UnLinkHash();
}

bool THashObj::LinkHash( THashObj* topObj )
{
	if( _priorHash )
		return false;
	this->_nextHash = topObj->_nextHash;
	this->_priorHash = topObj;
	topObj->_nextHash->_priorHash = this;
	topObj->_nextHash = this;
	return true;
}

bool THashObj::UnLinkHash()
{
	if( !_priorHash )
		return false;
	this->_priorHash->_nextHash = this->_nextHash;
	this->_nextHash->_priorHash = this->_priorHash;
	this->_nextHash = this->_priorHash = NULL;
	return true;
}

THashTbl::THashTbl( int hashNum, bool isDeleteObj )
: _hashTbl( NULL ), _registerNum( 0 ), _isDeleteObj( isDeleteObj )
{
	if( (_hashNum = hashNum ) > 0 )
		Init( hashNum );
}

THashTbl::~THashTbl()
{
	UnInit();
}

bool THashTbl::Init( int hashNum )
{
	assert( _hashTbl == NULL );
	_hashTbl = new THashObj[hashNum];
	if( _hashTbl == NULL )
		return false;
	for( int i = 0; i < hashNum; ++i )
	{
		THashObj* obj = _hashTbl + i;
		obj->_priorHash = obj->_nextHash = obj;
	}
	_registerNum = 0;
	return true;
}

void THashTbl::UnInit()
{
	if( _hashTbl )
	{
		if( _isDeleteObj )
		{
			for( int i = 0; i < _hashNum && _registerNum > 0; ++i )
			{
				THashObj* start = _hashTbl + i;
				for( THashObj* obj = start->_nextHash; obj != start; )
				{
					THashObj* next = obj->_nextHash;
					delete obj;
					obj = next;
					--_registerNum;
				}

			}
		}
		delete[] _hashTbl;
		_hashTbl = NULL;
		_registerNum = 0;
	}
}

void THashTbl::Register( THashObj* obj, unsigned hashId )
{
	obj->_hashId = hashId;
	if( obj->LinkHash( _hashTbl + ( hashId%_hashNum ) ) )
	{
		++_registerNum;
	}
}

void THashTbl::UnRegister( THashObj* obj )
{
	if( obj->UnLinkHash() )
		--_registerNum;
}

int THashTbl::GetRegisterNum()
{
	return _registerNum;
}

THashObj* THashTbl::Search( const void* data, unsigned hashId )
{
	THashObj* top = _hashTbl + ( hashId%_hashNum );
	for( THashObj* obj = top->_nextHash; obj != top; obj = obj->_nextHash )
	{
		if( obj->_hashId == hashId && IsSameVal( obj, data ) )
			return obj;
	}
	return NULL;
}

//////////////////////////////////////////////////////////////////////////
HINSTANCE defaultStrInstance = NULL;
static LCID defaultLCID;

void InitInstanceForLoadStr( HINSTANCE hInstance )
{
	defaultStrInstance = hInstance;
}

LPSTR GetLoadStrA( UINT resId, HINSTANCE hInstance )
{
	static TResHash* hash = NULL;
	if( hash == NULL )
		hash = new TResHash( 100 );

	char buf[1024] = { 0 };
	TResHashObj* obj = hash->Search( resId );
	if( obj == NULL )
	{
		HINSTANCE hi = hInstance ? hInstance : defaultStrInstance;
		if( ::LoadStringA( hi, resId, buf, sizeof(buf) ) >= 0 )
		{
			obj = new TResHashObj( resId, _strdup(buf) );
			hash->Register( obj );
		}
	}
	return obj ? (char*)obj->_val : NULL;
}

LPWSTR GetLoadStrW( UINT resId, HINSTANCE hInstance )
{
	static TResHash* hash = NULL;
	if( hash == NULL )
		hash = new TResHash( 100 );

	WCHAR buf[1024] = { 0 };
	TResHashObj* obj = hash->Search( resId );
	if( obj == NULL )
	{
		HINSTANCE hi = hInstance ? hInstance : defaultStrInstance;
		if( ::LoadStringW( hi, resId, buf, sizeof(buf)/sizeof(WCHAR) ) >= 0 )
		{
			obj = new TResHashObj( resId, _wcsdup(buf) );
			hash->Register( obj );
		}
	}
	return obj ? (LPWSTR)obj->_val : NULL;
}

HMODULE TLoadLibrary( LPSTR dllname )
{
	HMODULE	hModule = ::LoadLibrary( dllname );
	if ( defaultLCID )
		TSetThreadLocale(defaultLCID);
	return	hModule;
}
HMODULE TLoadLibraryV( void *dllname )
{
	HMODULE	hModule = TAPI32V::LoadLibraryV( dllname );
	if (defaultLCID)
		TSetThreadLocale(defaultLCID);
	return	hModule;
}

int MakePath( char* dest, const char* dir, const char* file )
{
	bool separetor = true;
	size_t len = strlen( dir );
	if( len == 0 )
		return wsprintf( dest, "%s", file );
	if( dir[len-1] == '\\' )
	{
		if( len > 2 && IsDBCSLeadByte( dir[len-2] ) == FALSE )
			separetor = false;
		else
		{
			u_char* p = (u_char*)dir;
			for( ; *p && p[1]; p += IsDBCSLeadByte( *p ) ? 2 : 1 );

			if( *p == '\\' ) separetor = false;
		}
	}
	return wsprintf( dest, "%s%s%s", dir, separetor ? "\\" : "", file );
}
int MakePathW( WCHAR *dest, const WCHAR *dir, const WCHAR *file )
{
	size_t len = wcslen( dir );
	if( len == 0 )
		return wsprintfW( dest, L"%s", file );
	return wsprintfW( dest, L"%s%s%s", dir, dir[len-1] == L'\\' ? L"" : L"\\", file );
}

WCHAR lGetCharIncW( const WCHAR** str )
{
	return *(*str)++;
}

WCHAR lGetCharIncA( const char** str )
{
	WCHAR ch = *(*str)++;
	if( IsDBCSLeadByte( (BYTE)ch ) )
	{
		ch <<= BITS_OF_BYTE;
		ch |= *(*str)++;
	}
	return ch;
}

WCHAR lGetCharW( const WCHAR* str, int offset )
{
	return str[offset];
}

WCHAR lGetCharA( const char* str, int offset )
{
	while ( offset-- > 0 )
		lGetCharIncA(&str);
	return	lGetCharIncA(&str);
}
void lSetCharW( WCHAR* str, int offset, WCHAR ch )
{
	str[offset] = ch;
}
void lSetCharA( char* str, int offset, WCHAR ch )
{
	while( offset-- > 0 )
	{
		if( IsDBCSLeadByte( *str++ ) )
			*str++;
	}
	BYTE high_ch = ch >> BITS_OF_BYTE;
	if( high_ch ) *str++ = high_ch;
	*str = (BYTE)ch;
}

void TSetDefaultLCID( LCID lcid )
{
	defaultLCID = lcid ? lcid : ::GetSystemDefaultLCID();
	TSetThreadLocale( defaultLCID );
}

BOOL TSetThreadLocale( int lcid )
{
	static bool once = false;
	static LANGID ( WINAPI* pfnSetThreadUILanguage )( LANGID langId );

	if( !once )
	{
		if( IsWinVista() )
		{
			pfnSetThreadUILanguage = 
				(LANGID ( WINAPI*)( LANGID ))::GetProcAddress( ::GetModuleHandle("kernel32"), "SetThreadUILanguage" );
		}
		once = true;
	}
	if( pfnSetThreadUILanguage )
	{
		pfnSetThreadUILanguage( LANGIDFROMLCID(lcid) );
	}
	return ::SetThreadLocale( lcid );
}

int strncmpi( const char* str1, const char* str2, int num )
{
	for( int cnt = 0; cnt < num; ++cnt )
	{
		char c1 = toupper( str1[cnt] );
		char c2 = toupper( str2[cnt] );
		if( c1 == c2 )
		{
			if( c1 ) continue;
			else return 0;
		}
		if( c1 > c2 ) return 1;
		else return -1;
	}
	return 0;
}

char* strncpyz( char* dest, const char* src, int num )
{
	char* sv = dest;
	while( num-- > 0 )
		if( (*dest++ == *src++ ) == '\0' )
			return sv;
	if( sv != dest ) *( dest - 1 ) = 0;
	return sv;
}

char* strdupNew( const char* s )
{
	int len = strlen( s ) + 1;
	char* tmpStr = new char[len];
	memcpy( tmpStr, s, len );
	return tmpStr;
}
WCHAR* wcsdupNew( const WCHAR* s )
{
	int len = wcslen( s ) + 1;
	WCHAR* tmpStr = new WCHAR[len];
	memcpy( tmpStr, s, len*sizeof(WCHAR) );
	return tmpStr;
}

__int64 hex2ll(char* buf)
{
	__int64 ret = 0;
	for( ; *buf; ++buf )
	{
		if( *buf >= '0' && *buf <= '9')
			ret = ( ret << 4 ) | ( *buf - '0' );
		else if( toupper(*buf) >= 'A' && toupper(*buf) <= 'F' )
			ret = ( ret << 4 ) | ( toupper(*buf) - 'A' + 10 );
		else continue;
	}
	return ret;
}